// STL includes
#include <vector>
#include <string>

// C includes

// Library includes
#include <cxxtest/TestSuite.h>

// Project includes
#include <Connector.h>

using namespace std;

class ConnectorTestSuite: public CxxTest::TestSuite
{
  struct Sender: public Connectable
  {
    void sendTest(void)
    {
      emit(1);
    }
    void sendFive(void)
    {
      emit(5);
    }
    void send(uint8_t signal)
    {
      emit(signal);
    }
    Sender(Connector& _conn): Connectable(_conn) {}
  };
  struct Receiver: public Connectable
  {
    int num_received;
    Receiver(Connector& _conn): Connectable(_conn), num_received(0) {}
    virtual void onNotify(const Connectable* ,uint8_t signal)
    {
      if ( signal == 1 )
	++ num_received;
      else if ( signal == 5 )
	num_received += 5;
    }
  };
  struct GenericReceiver: public Connectable
  {
    int num_received;
    GenericReceiver(Connector& _conn): Connectable(_conn), num_received(0) {}
    virtual void onNotify(const Connectable* ,uint8_t)
    {
      ++ num_received;
    }
  };
  struct Logger: public Connector::ILogger
  {
    const Connectable* sender;
    const Connectable* receiver;
    uint8_t signal;
    virtual void log_emit(const Connectable* _sender, uint8_t _signal)
    {
      sender = _sender;
      signal = _signal;
    }
    virtual void log_notify(const Connectable* _receiver)
    {
      receiver = _receiver;
    }
  };

  Connector* pConnector;
  Sender *pSender;
  Receiver *pReceiver;
  Logger* pLogger;
public:
  void setUp()
  {
    pConnector = new Connector;
    pSender = new Sender(*pConnector);
    pReceiver = new Receiver(*pConnector);
    pLogger = new Logger();
  }
  void tearDown()
  {
    delete pLogger;
    delete pReceiver;
    delete pSender;
    delete pConnector;
  }

  void testEmpty( void )
  {
    TS_ASSERT_EQUALS( pConnector->size(), 0 );
  }
  void testAdd( void )
  {
    pReceiver->listen(/*from*/pSender,/*signal*/1);
    TS_ASSERT_EQUALS( pConnector->size(), 1 );
  }
  void testSend( void )
  {
    pReceiver->listen(/*from*/pSender,/*signal*/1);
    pSender->sendTest();

    TS_ASSERT_EQUALS( pReceiver->num_received, 1 );
  }
  void testSendTwo( void )
  {
    pReceiver->listen(/*from*/pSender,/*signal*/1);
    pReceiver->listen(/*from*/pSender,/*signal*/5);
    pSender->sendTest();
    pSender->sendFive();

    TS_ASSERT_EQUALS( pReceiver->num_received, 6 );
  }
  void testSendTwoSame( void )
  {
    pReceiver->listen(/*from*/pSender,/*signal*/1);
    pReceiver->listen(/*from*/pSender,/*signal*/5);
    pSender->sendTest();
    pSender->sendTest();

    TS_ASSERT_EQUALS( pReceiver->num_received, 2 );
  }
  void testSendMissing( void )
  {
    pReceiver->listen(/*from*/pSender,/*signal*/1);
    pReceiver->listen(/*from*/pSender,/*signal*/0xff);
    pSender->sendTest();

    TS_ASSERT_EQUALS( pReceiver->num_received, 1 );
  }
  void testNullEmitter( void )
  {
    pReceiver->listen(/*from*/NULL,/*signal*/1);
    pSender->sendTest();
    
    TS_ASSERT_EQUALS( pReceiver->num_received, 1 );
  }
  void testNoListeners( void )
  {
    pSender->sendTest();
    TS_ASSERT_EQUALS( pReceiver->num_received, 0 );
  }
  void testListenAll( void )
  {
    pReceiver->listen(/*from*/NULL,/*signal*/1);
    pSender->sendTest();
    
    TS_ASSERT_EQUALS( pReceiver->num_received, 1 );
  }
  void testLogger( void )
  {
    pConnector->setLogger(pLogger);
    pReceiver->listen(/*from*/pSender,/*signal*/1);
    pSender->sendTest();

    TS_ASSERT_EQUALS( pLogger->sender, pSender );
    TS_ASSERT_EQUALS( pLogger->signal, 1 );
    TS_ASSERT_EQUALS( pLogger->receiver, pReceiver );
  }
  void testNoListenZero( void )
  {
    GenericReceiver generic(*pConnector); 
    generic.listen(/*from*/pSender,/*signal*/0);
    pSender->send(0);
    pSender->send(1);

    TS_ASSERT_EQUALS( generic.num_received, 0 );
  }
  void testUnListen( void )
  {
    pReceiver->listen(/*from*/pSender,/*signal*/1);
    pReceiver->unListen(/*from*/pSender);
    pSender->sendTest();

    TS_ASSERT_EQUALS( pReceiver->num_received, 0 );
  }
  void testReuseConnections_SizeOK( void )
  {
    pReceiver->listen(/*from*/pSender,/*signal*/2);
    pReceiver->unListen(/*from*/pSender);
    pReceiver->listen(/*from*/pSender,/*signal*/1);

    // Make sure it actually re-used the slot freed by unListen
    TS_ASSERT_EQUALS( pConnector->size(), 1 ); 
  }
  void testReuseConnections_Works( void )
  {
    pReceiver->listen(/*from*/pSender,/*signal*/2);
    pReceiver->unListen(/*from*/pSender);
    pReceiver->listen(/*from*/pSender,/*signal*/1);

    pSender->sendTest();

    // Make sure it still works 
    TS_ASSERT_EQUALS( pReceiver->num_received, 1 );
  }
};
// vim:cin:ai:sts=2 sw=2 ft=cpp
